package cn.com.acca.ma.dao.impl;

import cn.com.acca.ma.jdbctemplate.util.JdbcTemplateUtil;
import cn.com.acca.ma.xmlmapper.StockIndexWeekMapper;
import com.alibaba.druid.pool.DruidPooledCallableStatement;

import java.sql.*;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import oracle.jdbc.OracleCallableStatement;
import oracle.jdbc.OracleConnection;
import oracle.jdbc.OracleTypes;
import oracle.sql.ARRAY;
import oracle.sql.Datum;
import oracle.sql.STRUCT;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.log4j.Logger;
import org.hibernate.Session;

import cn.com.acca.ma.dao.BaseDao;
import cn.com.acca.ma.xmlmapper.ForeignExchangeMapper;
import cn.com.acca.ma.xmlmapper.ForeignExchangeRecordMapper;
import cn.com.acca.ma.xmlmapper.StockIndexMapper;
import cn.com.acca.ma.xmlmapper.StockIndexWeekendMapper;

import java.lang.reflect.ParameterizedType;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.CallableStatementCallback;
import org.springframework.jdbc.core.CallableStatementCreator;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;

public abstract class BaseDaoImpl<T> implements BaseDao {
	protected Class<T> entityClass;
	protected String className;
	
	/**
	 * Logger类的实例，用于记录日志
	 */
	protected Logger logger;
	
	/**
	 * 获取程序当前路径，即根目录
	 */
	protected static final String DIR=System.getProperty("user.dir");
	
	/**
	 * 从网络上获取四种指数的数据，将其存储在index_data.txt中，再删除STOCK_INDEX表中的数据，最后将index_data.txt中的数据插入到STOCK_INDEX表中
	 */
	protected static final String INDEX_DATE=DIR+"/src/main/resources/output/index_data.txt";
	
	/**
	 * JPA的persistence unit的名字
	 */
	protected static final String PERSISTENCE_UNIT_NAME="movingAverage";
	
	/**
	 * JPA的EntityManagerFactory类的实例
	 */
	protected EntityManagerFactory emf;
	
	/**
	 * JPA的EntityManager类的实例
	 */
	protected EntityManager em;
	
	/**
	 * myBatis的SqlSessionFactory类的实例
	 */
	protected SqlSessionFactory sqlSessionFactory;
	
	/**
	 * myBatis的SqlSession类的实例
	 */
	protected SqlSession sqlSession;

	/**
	 * jdbcTemplate的事务模板
	 */
	protected TransactionTemplate transactionTemplate;

	/**
	 * jdbcTemplate模板
	 */
	protected JdbcTemplate jdbcTemplate;

	/**
	 * namedParameterJdbcTemplate模板
	 */
	protected NamedParameterJdbcTemplate namedParameterJdbcTemplate;
	
	/**
	 * myBatis的StockIndexMapper类的实例
	 */
	protected StockIndexMapper stockIndexMapper;
	
	/**
	 * myBatis的StockIndexWeekMapper类的实例
	 */
	protected StockIndexWeekMapper stockIndexWeekMapper;

	/**
	 * myBatis的StockIndexWeekendMapper类的实例
	 */
	protected StockIndexWeekendMapper stockIndexWeekendMapper;
	
	/**
	 * myBatis的ForeignExchangeMapper类的实例
	 */
	protected ForeignExchangeMapper foreignExchangeMapper;
	
	/**
	 * myBatis的ForeignExchangeRecordMapper类的实例
	 */
	protected ForeignExchangeRecordMapper foreignExchangeRecordMapper;

	/**
	 * Hibernate的Session对象的实例
	 */
	protected Session session;
	
	@SuppressWarnings("unchecked")
	public BaseDaoImpl(){
		entityClass=(Class<T>)((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
		className=entityClass.getName();
		logger = Logger.getLogger(entityClass);
	}

	protected OracleConnection getOracleConnection() {
		OracleConnection conn = null;
		try {
			Class.forName("oracle.jdbc.driver.OracleDriver");
			try {
				conn = (OracleConnection) DriverManager.getConnection(
						"jdbc:oracle:thin:@127.0.0.1:1521:ADAM", "scott",
						"tiger");
				return conn;
			} catch (SQLException e) {
				e.printStackTrace();
			}
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		return conn;
	}

	/**
	 * jdbcTemplate执行sql
	 * @param sql
	 * @return
	 */
	protected Object doSQLInTransaction(String sql){
		transactionTemplate = JdbcTemplateUtil.getTransactionTemplate();
		jdbcTemplate = JdbcTemplateUtil.getJdbcTemplate();

		return transactionTemplate.execute(new TransactionCallback<Object>() {
			@Override
			public Object doInTransaction(TransactionStatus transactionStatus) {
				try {
					jdbcTemplate.execute(sql);
				} catch (Throwable e) {
					e.printStackTrace();
					transactionStatus.setRollbackOnly();
				}
				return null;
			}
		});
	}

	/**
	 * 根据sql、查询条件、返回类型，返回List类型数据
	 * @param sql
	 * @param map
	 * @param clazz
	 * @return
	 */
	protected List doSQLQueryInTransaction(String sql, Map map, Class clazz){
		namedParameterJdbcTemplate = JdbcTemplateUtil.getNamedParameterJdbcTemplate();
		transactionTemplate = JdbcTemplateUtil.getTransactionTemplate();
		return (List) transactionTemplate.execute(new TransactionCallback<Object>() {
			@Override
			public List doInTransaction(TransactionStatus transactionStatus) {
				try {
					return namedParameterJdbcTemplate.query(sql, map, new BeanPropertyRowMapper<T>(clazz));
				} catch (Throwable e) {
					e.printStackTrace();
					transactionStatus.setRollbackOnly();
					// transactionStatus.rollbackToSavepoint(savepoint);
				}
				return null;
			}
		});
	}

	/**
	 * 根据sql、查询条件、返回类型，返回Integer类型数据
	 * @param sql
	 * @param map
	 * @return
	 */
	protected Integer doSQLQueryInTransaction(String sql, Map map){
		namedParameterJdbcTemplate = JdbcTemplateUtil.getNamedParameterJdbcTemplate();
		transactionTemplate = JdbcTemplateUtil.getTransactionTemplate();
		return (Integer) transactionTemplate.execute(new TransactionCallback<Object>() {
			@Override
			public Integer doInTransaction(TransactionStatus transactionStatus) {
				try {
					return namedParameterJdbcTemplate.queryForInt(sql, map);
				} catch (Throwable e) {
					e.printStackTrace();
					transactionStatus.setRollbackOnly();
					// transactionStatus.rollbackToSavepoint(savepoint);
				}
				return null;
			}
		});
	}

	/**
	 * 调用存储过程，并返回值
	 * @param procedureSql
	 * @return
	 */
	public Object doProcedureInTransaction(String procedureSql, String outParameterType,
		Object[] inParameter, Class[] inParameterType) {
		jdbcTemplate = JdbcTemplateUtil.getJdbcTemplate();
		transactionTemplate = JdbcTemplateUtil.getTransactionTemplate();
		return transactionTemplate.execute(new TransactionCallback<Object>() {
			@Override
			public Object doInTransaction(TransactionStatus transactionStatus) {
				try {
					List<HashMap> result = (List<HashMap>) jdbcTemplate.execute(
						new CallableStatementCreator() {
							public CallableStatement createCallableStatement(Connection connection)
								throws SQLException {
								DruidPooledCallableStatement oracleCallableStatement = (DruidPooledCallableStatement) connection.prepareCall(procedureSql);
								if(null != inParameter && null != inParameterType){
									for (int i = 0; i < inParameter.length; i++) {
										if (inParameterType[i].getName().equals("java.lang.String")) {
											oracleCallableStatement.setString(i + 1, (String) inParameter[i]);
										}
										if (inParameterType[i].getName().equals("java.lang.Integer")) {
											oracleCallableStatement.setInt(i + 1, (Integer) inParameter[i]);
										}
									}
								}
								if (null != outParameterType) {
									oracleCallableStatement.registerOutParameter(inParameter.length + 1, OracleTypes.ARRAY,
											outParameterType);
								}
								return oracleCallableStatement;
							}
						}, new CallableStatementCallback() {
							public Object doInCallableStatement(CallableStatement callableStatement)
								throws SQLException, DataAccessException {
								callableStatement.execute();
								List<HashMap> list = new ArrayList<HashMap>();
								if (null != outParameterType){
									ARRAY array = (ARRAY) ((DruidPooledCallableStatement) callableStatement).getArray(inParameter.length + 1);
									Datum[] datas = (Datum[]) array.getOracleArray();
									if (null != datas) {
										for (int i = 0; i < datas.length; i++) {
											if (datas[i] != null && ((STRUCT) datas[i]) != null) {
												Datum[] datumArray = ((STRUCT) datas[i]).getOracleAttributes();
												HashMap hashMap = new HashMap();
												for (int j=0; j<datumArray.length; j++){
													if (datumArray[j].isConvertibleTo(java.sql.Date.class) ){
														hashMap.put(j, datumArray[j].dateValue());
													}
													if (datumArray[j].isConvertibleTo(java.math.BigDecimal.class) ){
														hashMap.put(j, datumArray[j].bigDecimalValue());
													}
												}
												list.add(hashMap);
											} else {
												logger.info("datas[" + i + "] is null.");
											}
										}
									}
								}
								return list;
							}
						});
					return result;
				} catch (Throwable e) {
					e.printStackTrace();
					transactionStatus.setRollbackOnly();
					// transactionStatus.rollbackToSavepoint(savepoint);
				}
				return null;
			}
		});
	}
}
